﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Windows.Forms;

namespace iControl
{
    sealed class KeyMapManager
    {
        #region Variable Declaration
        /// <summary>
        /// Used in lock-on block instead of class type to avoid deadlocks.
        /// </summary>
        private static object m_SyncRoot = new Object();
        #endregion

        #region Constructor
        /// <summary>
        /// Initializes KeyMapManager
        /// </summary>
        private KeyMapManager()
        {
            this.Reload();
        }
        #endregion

        #region Methods
        public void Reload()
        {
            if( System.IO.File.Exists(KeyMapFileLocation) )
            {
                try
                {
                    XElement xKeyMaps = XElement.Load(KeyMapFileLocation);
                    var keyMaps = from element in xKeyMaps.Elements(KeyMap.ElementName)
                                  select new KeyMap()
                                  {
                                      Command = (ControlCommand)Enum.Parse(typeof(ControlCommand), (string.IsNullOrEmpty(element.Attribute(KeyMap.CommandAttributeName).Value)) ? "None" : element.Attribute(KeyMap.CommandAttributeName).Value, true),
                                      Key = (Keys)Enum.Parse(typeof(Keys), (string.IsNullOrEmpty(element.Attribute(KeyMap.KeyAttributeName).Value)) ? "None" : element.Attribute(KeyMap.KeyAttributeName).Value),
                                      Modifiers = (Keys)Enum.Parse(typeof(Keys), (string.IsNullOrEmpty(element.Attribute(KeyMap.ModifierAttributeName).Value)) ? "None" : element.Attribute(KeyMap.ModifierAttributeName).Value)
                                  };

                    this.KeyMaps = keyMaps.ToList();
                    return;
                }
                catch( Exception ex )
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                    System.Diagnostics.Debug.WriteLine(ex.StackTrace);
                }
            }
            else
            {
                SaveDefaultKeyMaps();
            }
        }

        /// <summary>
        /// Gets command associated to the specified key and modifiers.
        /// </summary>
        public ControlCommand GetCommand(Keys key, Keys modifiers)
        {
            return GetCommand(key | modifiers);
        }
        /// <summary>
        /// Gets command associated to the specified complete key. Complete key is bitwise combination of modifiers and key value.
        /// </summary>
        /// <param name="completeKey"></param>
        /// <returns></returns>
        public ControlCommand GetCommand(Keys completeKey)
        {
            ControlCommand selectedCommand = ControlCommand.None;

            if( KeyMaps != null )
            {
                foreach( var keyMap in KeyMaps )
                {
                    if( keyMap.CompleteKey == completeKey )
                    {
                        selectedCommand = keyMap.Command;
                        break;
                    }
                }
            }

            return selectedCommand;
        }

        /// <summary>
        /// Saves all KeyMaps to the specified location.
        /// </summary>
        /// <param name="location"></param>
        public void SaveToFile()
        {
            if( KeyMaps == null )
                return;

            if( System.IO.File.Exists(KeyMapFileLocation) )
                System.IO.File.Delete(KeyMapFileLocation);

            var xTree = new XElement("KeyMaps");
            foreach( var keyMap in KeyMaps )
            {
                xTree.Add(keyMap.ToXElement());
            }

            xTree.Save(KeyMapFileLocation);
        }

        public void SaveDefaultKeyMaps()
        {
            if( !this.Initialized )
                this.KeyMaps = new List<KeyMap>();
            else
                this.KeyMaps.Clear();

            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.PlayPause, Key = Keys.Home, Modifiers = Keys.Control | Keys.Alt });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.Play, Key = Keys.F11, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.NextTrack, Key = Keys.PageDown, Modifiers = Keys.Control | Keys.Alt });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.BackTrack, Key = Keys.PageUp, Modifiers = Keys.Control | Keys.Alt });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.PreviousTrack, Key = Keys.OemMinus, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.Rewind, Key = Keys.Left, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.FastForward, Key = Keys.Right, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.Stop, Key = Keys.End, Modifiers = Keys.Control | Keys.Alt });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.NextAlbum, Key = Keys.PageDown, Modifiers = Keys.Control | Keys.Alt | Keys.Shift});
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.PreviousAlbum, Key = Keys.PageUp, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.ToggleAllSongsRepeat, Key = Keys.F9, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.ToggleShuffle, Key = Keys.F10, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.ShowHideCurrentTrackInfo, Key = Keys.I, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });

            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.IncreaseVolume, Key = Keys.Up, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.DecreaseVolume, Key = Keys.Down, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.ToggleMute, Key = Keys.Add, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.IncreaseMasterVolume, Key = Keys.Add, Modifiers = Keys.Control | Keys.Alt });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.DecreaseMasterVolume, Key = Keys.Subtract, Modifiers = Keys.Control | Keys.Alt });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.ToggleMuteMasterVolume, Key = Keys.Subtract, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });

            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.Rate0, Key = Keys.D0, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.Rate1, Key = Keys.D1, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.Rate2, Key = Keys.D2, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.Rate3, Key = Keys.D3, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.Rate4, Key = Keys.D4, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.Rate5, Key = Keys.D5, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });

            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.OpenSettingsWindow, Key = Keys.F12, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.OpenQuickSelectWindow, Key = Keys.OemSemicolon, Modifiers = Keys.Control | Keys.Alt });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.OpenAboutWindow, Key = Keys.F1, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });
            this.KeyMaps.Add(new KeyMap() { Command = ControlCommand.Quit, Key = Keys.F4, Modifiers = Keys.Control | Keys.Alt | Keys.Shift });

            this.SaveToFile();
        }
        #endregion

        #region Static Properties
        private static volatile KeyMapManager m_DefaultInstance;
        /// <summary>
        /// Gets default instance of Settings class.
        /// </summary>
        public static KeyMapManager DefaultInstance
        {
            get
            {
                if( m_DefaultInstance == null )
                {
                    //This double-check locking approach solves the thread concurrency problems while avoiding an exclusive 
                    //lock in every call to the Instance property method.
                    lock( m_SyncRoot )
                    {
                        if( m_DefaultInstance == null )
                            m_DefaultInstance = new KeyMapManager();
                    }
                }

                return m_DefaultInstance;
            }
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets a collection of all KeyMap objects.
        /// </summary>
        public List<KeyMap> KeyMaps { get; private set; }

        /// <summary>
        /// Gets a value indicating whether KeyMapManager is initialized. KeyMapManager must be initialized before 
        /// using any of its method or properties.
        /// </summary>
        public bool Initialized { get { return (this.KeyMaps != null); } }

        public static string KeyMapFileLocation
        {
            get { return Application.StartupPath + "\\KeyMaps.xmap"; }
        }
        #endregion
    }
}
